﻿#include "CELLIOCPServer.h"

#ifdef _WIN32

#include "CELLClient.h"
#include "CELLIOCP.hpp"
#include "CELLNetWork.hpp"

CELLIOCPServer::CELLIOCPServer()
{
	_iocp.create();
}

CELLIOCPServer::~CELLIOCPServer()
{
	Close();
}

bool CELLIOCPServer::DoNetEvents()
{
	CELLClient* pClient = nullptr;
	// 检测需要发送给客户端数据的客户端(这个可以根据情况而改变)
	for (auto iter = _clients.begin(); iter != _clients.end(); )
	{
		pClient = iter->second;
		if (pClient->needWrite())
		{
			auto pIoData = pClient->makeSendIOData();
			if (pIoData)
			{
				if (!_iocp.postSend(pIoData))
				{
					OnClientLeave(pClient);
					iter = _clients.erase(iter);
					continue;
				}
			}
			//--
			pIoData = pClient->makeRecvIOData();
			if (pIoData)
			{
				if (!_iocp.postRecv(pIoData))
				{
					OnClientLeave(pClient);
					iter = _clients.erase(iter);
					continue;
				}
			}
		}
		else {
			auto pIoData = pClient->makeRecvIOData();
			if (pIoData)
			{
				if (!_iocp.postRecv(pIoData))
				{
					OnClientLeave(pClient);
					iter = _clients.erase(iter);
					continue;
				}
			}
		}
		iter++;
	}

	// 循环处理事件直到没有事件发生为止
	while (true)
	{
		int ret = DoNetIOCPEvents();
		if (ret < 0)
		{
			// IOCP出错
			return false;
		}
		else if (ret == 0)
		{
			// 没有事件发生
			return true;
		}
	}
	
	return true;
}

// 每次只处理一件网络事件
/*
	返回值 ret：
	   -1: iocp出错
		0: 没有事件发生
		1: 有事件发生
*/
int CELLIOCPServer::DoNetIOCPEvents()
{
	int ret = _iocp.wait(_ioEvent, 1);
	if (ret < 0)
	{
		// IOCP出错
		CELLLog_Error("CELLIOCPServer::DoNetEvents");
		return ret;
	}
	else if (ret == 0)
	{
		// 没有事件发生
		return ret;
	}

	// 接收数据
	if (IO_TYPE::RECV == _ioEvent.pIOData->iotype)
	{// 接收数据完成
		if (_ioEvent.bytesTrans <= 0)
		{// 客户端断开
			CELLLog_Info("RECV close sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
			rmClient(_ioEvent);
			return ret;
		}
		// 告诉接收数据的客户端本次接收到了多少数据，用于接收缓冲区的数据位置的偏移
		CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
		if (pClient)
			pClient->recv4Iocp(_ioEvent.bytesTrans);
		// 接收消息计数
		OnNetRecv(pClient);
		// CELLLog_Info("recv data: sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
	}
	else if (IO_TYPE::SEND == _ioEvent.pIOData->iotype)
	{// 发送数据完成
		if (_ioEvent.bytesTrans <= 0)
		{// 客户端断开
			CELLLog_Info("SEND close sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
			rmClient(_ioEvent);
			return ret;
		}
		// 通知发送缓冲区发送了多少数据
		CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
		if (pClient)
			pClient->send2Iocp(_ioEvent.bytesTrans);
		// CELLLog_Info("send data: sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
	}
	else
	{
		//
		CELLLog_Error("undefined iotype");
	}
	return ret;
}

// remove Client
void CELLIOCPServer::rmClient(IO_EVENT ioEvent)
{
	// 在心跳检测如果检测到了并关闭了socket，会使IOCP收到客户端断开的事件，
	// 而且在心跳检测哪里只关闭了socket，并没有释放client，这里就能直接获取到
	// 在rmClient函数中调用了客户端离开事件，在这里会释放client空间
	CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
	if (pClient)
		rmClient(pClient);
}

// 真正地删除客户端并触发离开事件
void CELLIOCPServer::rmClient(CELLClient* pClient)
{
	auto iter = _clients.find(pClient->sockfd());
	if (iter != _clients.end())
		_clients.erase(iter);
	//
	OnClientLeave(pClient);
}

// 本类中加入客户端加入事件
void CELLIOCPServer::OnClientJoin(CELLClient* pClient)
{
	_iocp.reg(pClient->sockfd(), (void*)pClient);
	// 获取本次要接收的数据存放在哪里
	auto pIoData = pClient->makeRecvIOData();
	// 如果获取到了(接收缓冲区有空间)就向IOCP投递接收数据的任务
	if (pIoData)
		_iocp.postRecv(pIoData);
}

#endif
